// SUGARSCAPE MODEL FOR SWARM
// Based on Nelson Minar's 1996-1997 model; modified by 
//  Andre Manoel (andremanoel@gmail.com) with research  
// purposes. See file README for more information.

#include "limits.h"
#import "SugarAgent.h"
#import <simtools.h>
#import "ModelSwarm.h"
#import <gui.h>

@implementation SugarAgent

// One "step" for an agent. Depending on the rules in effect, this step
//   could have a lot of different meanings
- step
{
  // The agent moves to a nearby spot
  [self moveToBestOpenSpot];

  // Aspects of an agents lifecycle - eating, metabolism, death
  // Eat the sugar and spice at the current spot
  float sugarIncrement, spiceIncrement;
  SugarValue sugarAt = [sugarSpace getSugarAtX: x Y: y];
  SpiceValue spiceAt = [sugarSpace getSpiceAtX: x Y: y];
  
  if (currentSugar + sugarAt > maxSugar) { // Gather only 'til max. capacity
    sugarIncrement =
      [sugarSpace takeSugar: (maxSugar - currentSugar) AtX: x Y: y];
  }
  else sugarIncrement = [sugarSpace takeSugarAtX: x Y: y];
  
  if (currentSpice + spiceAt > maxSpice) {
    spiceIncrement = 
      [sugarSpace takeSpice: (maxSpice - currentSpice) AtX: x Y: y]; 
  }
  else spiceIncrement = [sugarSpace takeSpiceAtX: x Y: y];
  
  currentSugar += sugarIncrement;
  currentSpice += spiceIncrement;

  // Spend the sugar and spice we need to stay alive
  currentSugar -= sugarMetabolism;
  currentSpice -= spiceMetabolism;

  // Send to SugarSpace information to update pollution
  [sugarSpace addPollutionForS: sugarIncrement 
                          forM: sugarMetabolism atX: x Y: y];

  // I'm now one year older
  age++;

  // Check if I'm on the childbearing age
  if ((age >= pubertyAge && age <= infertilityAge) 
      && (currentSugar >= initialSugar)
      && (currentSpice >= initialSpice)) fertile = 1;
  else fertile = 0;
  
  // The disease has its cycle
  [self diseaseCycle];

  // Interact with neighbours
  if (fertile == 1 || transmitCulture == 1 || allowTrading == 1 || diseaseOn == 1) 
    [self interactNeighbours];
    
  // Check if I'm dying
  if (currentSugar < 0 || currentSpice < 0 || age >= deathAge)
    {
      
      // If inheritance is allowed, and I have children and sugar,
      //    distribute the sugar among the children (not working 
      //    correctly, currently)
      int toEach; // typecast (id) won't work with float toEach
      if (inheritanceOn == 1 && [childList getCount] > 0 && currentSugar > 0) {
        toEach = currentSugar/[childList getCount];
        [childList forEach: M(gainSugar:) :
                            (id) toEach];
        printf ("I have %d children and gave each %d\n",
                [childList getCount], toEach);
      }

      [sugarSpace removeAgent: self];
      [modelSwarm agentDeath: self];
    }
    
  return self;
}

//// Mixing here movement rule M with combat rule C_alpha. When combat is
////   absent, it works as the movement rule

// A dumb macro to get the absolute value of an integer
#define intabs(a) ((a) < 0 ? -(a) : (a))

- moveToBestOpenSpot
{
  int xLook, yLook;
  float reward, maxReward;
  int retaliation;
  int bestDistance;
  int goodSpots, goodX[16], goodY[16];		  
  int chosenSpot, newX, newY;
  SugarAgent *agent;
  
  // Prime the algorithm with out of range values
  maxReward = -1;
  goodSpots = 0;
  bestDistance = 99999;
  
  retaliation = 0;
  for (xLook = x - vision; xLook <= x + vision; xLook++) {
    for (yLook = y - vision; yLook <= y + vision; yLook++) {
      agent = [sugarSpace getAgentAtX: xLook Y: yLook];
      if (agent != nil && [agent getTribe] != [self getTribe] &&
          agent->currentSugar > currentSugar) retaliation = 1;
    }
  }
  
  // First, look in the X direction for good spots
  yLook = y;
  for (xLook = x - vision; xLook <= x + vision; xLook++) {
    
    agent = [sugarSpace getAgentAtX: xLook Y: yLook];
    if (agent == nil || permitCombat == 1) {
      reward = [self getWelfareWithSugar: 
                       [sugarSpace getSugarAtX: xLook Y: yLook] 
                               withSpice: 
                       [sugarSpace getSpiceAtX: xLook Y: yLook]
                               withPollut:
		       [sugarSpace getPollutAtX: xLook Y: yLook]];
    
      // A bigger reward if there's an weaker agent on that spot
      if (permitCombat == 1 && agent != nil &&
          [agent getTribe] != [self getTribe] &&
          agent->currentSugar < currentSugar && retaliation == 0) {
        if (agent->currentSugar < combatPrize) 
          reward += agent->currentSugar;
        else reward += combatPrize;
      }

      // Is the spot we're looking at the best we've ever seen?
      if (reward > maxReward) {
        // Yes, best spot ever. Forget everything else, record this
        //   as the only good spot
        maxReward = reward;
        bestDistance = intabs(x - xLook);
        goodSpots = 0;
        goodX[0] = xLook;
        goodY[0] = yLook;
        goodSpots++;
      }
      else if (reward == maxReward) {
        // No, it's only as good as anything else we've seen. Is it closer
        //   than any other spot we've seen with this sugar?
        if (intabs(x - xLook) < bestDistance) {
        // Yes, forget all the rest - this is the only good spot
          bestDistance = intabs(x - xLook);
          goodSpots = 0;
          goodX[0] = xLook;
          goodY[0] = yLook;
          goodSpots++;
        }
        else if (intabs (x - xLook) == bestDistance) {
          // No, this spot is as good as some other one. Just add this
          //   one on as a good spot
          goodX[goodSpots] = xLook;
          goodY[goodSpots] = yLook;
          goodSpots++;
        }
      } 
    }
  }
  
  // Now repeat the same choice in the Y axis
  xLook = x;
  for (yLook = y - vision; yLook <= y + vision; yLook++) {
    
    agent = [sugarSpace getAgentAtX: xLook Y: yLook];
    
    if (agent == nil || permitCombat == 1) {
      reward = [self getWelfareWithSugar: 
                       [sugarSpace getSugarAtX: xLook Y: yLook] 
                               withSpice: 
                       [sugarSpace getSpiceAtX: xLook Y: yLook]
                               withPollut:
                       [sugarSpace getPollutAtX: xLook Y: yLook]];
       
      // A bigger reward if there's an weaker agent on that spot
      if (permitCombat == 1 && agent != nil &&
          [agent getTribe] != [self getTribe] &&
          agent->currentSugar < currentSugar && retaliation == 0) {
        if (agent->currentSugar < combatPrize) 
          reward += agent->currentSugar;
        else reward += combatPrize;
      }

      // Is the spot we're looking at the best we've ever seen?
      if (reward > maxReward) {
        // Yes, best spot ever. Forget everything else, record this
        //   as the only good spot
        maxReward = reward;
        bestDistance = intabs(y - yLook);
        goodSpots = 0;
        goodX[0] = xLook;
        goodY[0] = yLook;
        goodSpots++;
      }
      else if (reward == maxReward) {
        // No, it's only as good as anything else we've seen. Is it closer
        //   than any other spot we've seen with this sugar?
        if (intabs(y - yLook) < bestDistance) {
        // Yes, forget all the rest - this is the only good spot
          bestDistance = intabs(y - yLook);
          goodSpots = 0;
          goodX[0] = xLook;
          goodY[0] = yLook;
          goodSpots++;
        }
        else if (intabs (y - yLook) == bestDistance) {
          // No, this spot is as good as some other one. Just add this
          //   one on as a good spot
          goodX[goodSpots] = xLook;
          goodY[goodSpots] = yLook;
          goodSpots++;
        }
      } 
    }
  }
 
  // Alright, goodX[] and goodY[] record the best spots we've found
  // Let's figure out where to move

  if (goodSpots == 0);				  // no good spots, don't move
  else {
    if (goodSpots == 1)	chosenSpot = 0;		  // only one good spot
    else 					  // pick a random spot
      chosenSpot = [uniformIntRand getIntegerWithMin: 0 withMax: goodSpots-1];
      
    newX = goodX[chosenSpot];			  // get the coordinate
    newY = goodY[chosenSpot];			  // and move there!
    
    if ([sugarSpace getAgentAtX: newX Y: newY] != nil) {
      if ([sugarSpace getAgentAtX: newX Y: newY]->currentSugar < combatPrize)
        currentSugar += [sugarSpace getAgentAtX: newX Y: newY]->currentSugar;
      else currentSugar += combatPrize; 

      [modelSwarm agentDeath: [sugarSpace getAgentAtX: newX Y: newY]];
      [sugarSpace removeAgent: [sugarSpace getAgentAtX: newX Y: newY]];
    }
    
    [sugarSpace moveAgent: self toX: newX Y: newY];
  }
  
  return self;
}

//// Interact with neighbours: make sex, exchange cultural preferences, trade
- interactNeighbours
{
  SugarAgent *neighbour;
  
  int posIncr[2][4], rndIndex, usedIndexes[4], alreadyUsed;
  int emptySpot[2][6], emptySpots;
  int bX, bY, bVis, bFor, bTag[11]; // baby characteristics 
  float bEndow1, bEndow2, bMet1, bMet2;
  int i, n, rndNumber;
  
  // Define position increments to reach all four (von Neumann) neighbours
  posIncr[0][0] = 0; posIncr[1][0] = 1;
  posIncr[0][1] = 0; posIncr[1][1] = -1;
  posIncr[0][2] = 1; posIncr[1][2] = 0;
  posIncr[0][3] = -1; posIncr[1][3] = 0;
 
  // Restart counters
  turnSumPrices = 0;
  turnProdPrices = 1;
  turnVolume = 0;
  turnTrades = 0;
 
  // Interact with all the four neighbours
  for (n = 0; n < 4; n++) {
  
    // Choose random neighbour
    do {
      rndIndex = [uniformIntRand getIntegerWithMin: 0 withMax: 3];
      
      alreadyUsed = 0;  
      for (i = 0; i < n; i++)
        if (rndIndex == usedIndexes[i]) alreadyUsed = 1;
    }
    while (alreadyUsed == 1);
    usedIndexes[n] = rndIndex;

    neighbour = [sugarSpace getAgentAtX: x + posIncr[0][rndIndex] 
                 Y: y + posIncr[1][rndIndex]];                
    if (neighbour != nil) {
           
      // Sex rule, p. 56
      // Interact with neighbour if both fertile and of opp. sex
      if (fertile * neighbour->fertile == 1 && sex != neighbour->sex) {
        
        // Search for empty spots around me and my neighbour
        emptySpots = 0;
        for (i = 0; i < 4; i++) {
          if ([sugarSpace getAgentAtX: x + posIncr[0][i]
               Y: y + posIncr[1][i]] == nil) {
            emptySpot[0][emptySpots] = x + posIncr[0][i];
            emptySpot[1][emptySpots] = y + posIncr[1][i];
            emptySpots++;
          }
          if ([sugarSpace getAgentAtX: neighbour->x + posIncr[0][i] 
              Y: neighbour->y + posIncr[1][i]] == nil) {
            emptySpot[0][emptySpots] = neighbour->x + posIncr[0][i];
            emptySpot[1][emptySpots] = neighbour->y + posIncr[1][i];
            emptySpots++;
          }
        }

        // If an empty spot has been found, the baby will birth
        if (emptySpots > 0) {
      
          // Defines the baby characteristics
          rndNumber = [uniformIntRand getIntegerWithMin: 0 withMax: emptySpots-1];
          bX = emptySpot[0][rndNumber];
          bY = emptySpot[1][rndNumber];
                    
          bEndow1 = (initialSugar + neighbour->initialSugar)/2;
          bEndow2 = (initialSpice + neighbour->initialSpice)/2;
          currentSugar -= initialSugar/2;
          currentSpice -= initialSpice/2;
          neighbour->currentSugar -= neighbour->initialSugar/2;
          neighbour->currentSpice -= neighbour->initialSpice/2;
      
          if ([uniformIntRand getIntegerWithMin: 0 withMax: 1] == 0) 
            bVis = vision; 
          else bVis = neighbour->vision;
      
          if ([uniformIntRand getIntegerWithMin: 0 withMax: 1] == 0) 
            bFor = foresight; 
          else bFor = neighbour->foresight;
      
          if ([uniformDblRand getDoubleWithMin: 0 withMax: 1] == 0) 
            bMet1 = sugarMetabolism; 
          else bMet1 = neighbour->sugarMetabolism;
          
          if ([uniformDblRand getDoubleWithMin: 0 withMax: 1] == 0) 
            bMet2 = spiceMetabolism; 
          else bMet2 = neighbour->spiceMetabolism;
      
          for (i = 0; i < 11; i++) {
            if ([uniformIntRand getIntegerWithMin: 0 withMax: 1] == 0) 
              bTag[i] = tagString[i]; 
            else bTag[i] = neighbour->tagString[i];
          }
        
          [modelSwarm addNewAgentAtX: bX Y: bY wSugarEndow: bEndow1 
                      wSpiceEndow: bEndow2 wVision: bVis wForesight: bFor
                      wSugarMetabol: bMet1 wSpiceMetabol: bMet2 wTag: bTag];

          if (inheritanceOn == 1)
            [childList addLast: [sugarSpace getAgentAtX: bX Y: bY]];
        } 
      }
    
      // Culture transmission rule K, p. 73
      // Select a random tag and flip neighbour's one if different
      rndNumber = [uniformIntRand getIntegerWithMin: 0 withMax: 10];
      if (neighbour->tagString[rndNumber] != tagString[rndNumber])
        neighbour->tagString[rndNumber] = tagString[rndNumber];

      // Trading rule T, p. 105
      if (allowTrading == 1) {
        float myWel, neighWel, myNewWel, neighNewWel;
        float myMrs, neighMrs, myNewMrs, neighNewMrs;
        float price, sugarQuant = 0, spiceQuant = 0;
        int keepTrading = 1;

        while (keepTrading == 1) {

          // Calculate mine and neighbour's welfare and MRS
          myWel = [self getWelfare];
          neighWel = [neighbour getWelfare];
          myMrs = [self getMrs];
          neighMrs = [neighbour getMrs];

          // Calculate price and quantities
          price = sqrt(myMrs * neighMrs);
          if (price > 1) { sugarQuant = 1; spiceQuant = price; }
          else if (price < 1) { sugarQuant = 1/price; spiceQuant = 1; }
     
          // The default is myMrs > neighMrs. If myMrs < neighMrs,
          //   change signs. If myMrs == neighMrs, stop negotiation
          if (myMrs < neighMrs) { 
            sugarQuant *= -1;
            spiceQuant *= -1;
          }
          else if (myMrs == neighMrs) keepTrading = 0;
 
          // Calculate new welfare and MRS
          myNewMrs = [self getMrsWithSugar: sugarQuant 
                                 withSpice: -spiceQuant];
          myNewWel = [self getWelfareWithSugar: sugarQuant
                                     withSpice: -spiceQuant
                                     withPollut: 0];
          neighNewMrs = [neighbour getMrsWithSugar: -sugarQuant
                                         withSpice: spiceQuant];
          neighNewWel = [neighbour getWelfareWithSugar: -sugarQuant
                                               withSpice: spiceQuant
                                               withPollut: 0];
 
          // If the MRS didn't cross and the new welfares are greater than 
          // the old ones, the trading will occur
          if (((myMrs > neighMrs && myNewMrs > neighNewMrs) ||
              (myMrs < neighMrs && myNewMrs < neighNewMrs)) &&
              (myNewWel > myWel && neighNewWel > neighWel)) {
            [self gainSugar: sugarQuant andSpice: -spiceQuant];
            [neighbour gainSugar: -sugarQuant andSpice: spiceQuant];
            //printf ("Gave %f sugar, received %f spice.\n mrs %f -> %f, 
            //neighMrs %f -> %f.\n welfare %f -> %f, neighWelfare %f -> %f\n* ",
            //sugarQuant, spiceQuant, myMrs, myNewMrs, neighMrs, neighNewMrs, 
            //myWel, myNewWel, neighWel, neighNewWel);

            turnSumPrices += price;
            turnProdPrices *= price;
            turnVolume += (fabs(sugarQuant) + fabs(spiceQuant));
            turnTrades++;
          }
       
          else keepTrading = 0;
        }
      }

      // Disease transmission
      if (diseaseOn == 1 && numActDiseases != 0) {
        rndNumber = [uniformIntRand getIntegerWithMin: 0 withMax: numActDiseases-1];
        [neighbour activDisease: actDiseases[rndNumber]];
      }

    }
  }

  return self;
}

// Disease cycle
- diseaseCycle
{
  int i, j, k;
  float metabolIncrem = 0; 
  int sizeDisease, isSubstring;
  int hammDist, smlHammDist = 10, startOfIt = 0, changed;

  // Check for active diseases
  numActDiseases = 0;
  for (i = 0; i < numDiseases; i++) {
    if (diseaseList[i][0] == 1) {
      actDiseases[numActDiseases] = i;
      numActDiseases++;
    }
  }

  // Fight diseases
  for (i = 0; i < numActDiseases; i++) {

    // Check if the disease actDiseases[i] is a substring of immune system
    sizeDisease = diseaseList[actDiseases[i]][2];

    isSubstring = 0;
    for (j = 0; j <= 50 - sizeDisease && isSubstring == 0; j++) {
      for (k = 0; k < sizeDisease &&
                  diseaseList[actDiseases[i]][k+5] == immuneSystem[k+j];
                  k++);
      if (k == sizeDisease) {
        isSubstring = 1;
        diseaseList[actDiseases[i]][0] = 0;
        diseaseList[actDiseases[i]][3] = 0;
      }
    }

    // If it isn't a substring, fight the disease
    if (isSubstring == 0) {
      metabolIncrem += diseaseList[actDiseases[i]][1];

      // Search for the substring with less Hamming dist. to the immune system
      if (diseaseList[actDiseases[i]][3] == 0) {
        for (j = 0; j <= 50 - sizeDisease; j++) {
          hammDist = 0;
          for (k = 0; k < sizeDisease; k++)
            if (diseaseList[actDiseases[i]][k+5] != immuneSystem[k+j]) hammDist++;
          if (hammDist < smlHammDist) {
            smlHammDist = hammDist;
            diseaseList[actDiseases[i]][3] = 1;
            diseaseList[actDiseases[i]][4] = j;
          }
        }
      }

      // Change the immune system to fit the disease
      changed = 0;
      startOfIt = diseaseList[actDiseases[i]][4];
      for (j = 0; j < sizeDisease && changed == 0; j++) {
        if (diseaseList[actDiseases[i]][j+5] != immuneSystem[startOfIt+j]) {
          immuneSystem[startOfIt+j] = diseaseList[actDiseases[i]][j+5];
          changed = 1;
        }
      }
    }     
  }

  sugarMetabolism = normalSugMet + metabolIncrem;

  return self;
}

//// The code below here is not very interesting - it has to do with
////   the technical details of managing the data in the model, not the
////   modelling itself. You can safely ignore it until you want to know
////   the details of how the program works

// Setters and getters for various values
- setModelSwarm: s
{
  modelSwarm = s;
  sugarSpace = [s getSugarSpace];
  return self;
}

- setInitialSugar: (SugarValue)s
{
  initialSugar = s;
  currentSugar = s;
  return self;
}

- setSugarMetabolism: (SugarValue)m
{
  sugarMetabolism = m;
  normalSugMet = m;
  return self;
}

- setInitialSpice: (SpiceValue)s
{
  initialSpice = s;
  currentSpice = s;
  return self;
}

- setSpiceMetabolism: (SpiceValue)m
{
  spiceMetabolism = m;
  normalSpiMet = m;
  return self;
}

- gainSugar: (SugarValue)su andSpice: (SpiceValue)sp
{
  currentSugar += su;
  currentSpice += sp;
  return self;
}

- setVision: (int)v
{
  vision = v;
  return self;
}

- setForesight: (int)f
{
  foresight = f;
  return self;
}

- setDeathAge: (int)s
{
  deathAge = s;
  return self;
}

- setSex: (int)s;
{
  sex = s;
  return self;
}

- setPubertyAge: (int)a
{
  pubertyAge = a;
  return self;
}

- setInfertilityAge: (int)a
{
  infertilityAge = a;
  return self;
}

- createChildList
{
  inheritanceOn = 1;
  childList = [List create: [self getZone]];
  return self;
}

- setTransmitCulture: (int)f
{
  transmitCulture = f;
  return self;
}

- setTagStringAt: (int)i withValue: (int)t;
{
  tagString[i] = t;
  return self;
}

- setPermitCombat: (int)f withPrize: (int)p
{
  permitCombat = f;
  combatPrize = p;
  return self;
}

- setAllowTrading: (int)f
{
  allowTrading = f;
  return self;
}

- setDiseaseOn: (int)d withAsMuchAs: (int)n
{
  diseaseOn = d;
  numDiseases = n;
  return self;
}

- setImmuneStringAt: (int)i withValue: (int)v;
{
  immuneSystem[i] = v;
  return self;
}

- setDiseaseListLin: (int)i Col: (int)j withValue: (int)d;
{
  diseaseList[i][j] = d;
  return self;
}

- activDisease: (int)d
{
  diseaseList[d][0] = 1;
  return self;
}

- resetDistance;
{
  initX = x;
  initY = y;
  return self;
}

- setMaxSugar: (float)m1 MaxSpice: (float)m2
{
  maxSugar = m1;
  maxSpice = m2;
  return self;
}

- (int)getVision
{
  return vision;
}

- (int)getForesight
{
  return foresight;
}

- (int)getAge
{
  return age;
}

- (int)getSex
{
  return sex;
}

- (int)getTagNumberOfZeros
{
  int i, n0 = 0;
  for (i = 0; i < 11; i++) if (tagString[i] == 0) n0++;
  return n0;
}

- (int)getTribe
{
  int n0 = [self getTagNumberOfZeros], n1, majority;

  n1 = 11 - n0;
  if (n0 > n1) majority = 0;
  else majority = 1;
  
  return majority;
}

- (SugarValue)getCurrentSugar
{
  return currentSugar;
}

- (SugarValue)getSugarMetabolism
{
  return sugarMetabolism;
}

- (SpiceValue)getCurrentSpice
{
  return currentSpice;
}

- (SpiceValue)getSpiceMetabolism
{
  return spiceMetabolism;
}

- (float)getWelfare
{
  float fact1, fact2, total;
  float welfare;

  if (allowTrading == 0 || transmitCulture == 0) {
    fact1 = 1;
    fact2 = 1;
  }
  else {
    fact1 = [self getTagNumberOfZeros];
    fact1 /= 11;
    fact2 = 1 - fact1;
  }
  total = (sugarMetabolism * fact1 + spiceMetabolism * fact2);
  
  welfare = pow(currentSugar, 
               sugarMetabolism * fact1 / total) * 
            pow(currentSpice, 
               spiceMetabolism * fact2 / total);
  return welfare;
}

- (float)getMrs
{
  float mrs;
  mrs = (currentSpice/spiceMetabolism)/(currentSugar/sugarMetabolism);
  return mrs;
}

- (float)getWelfareWithSugar: (SugarValue)su withSpice: (SpiceValue)sp
                   withPollut: (float)pol;
{
  float fakeSugar, fakeSpice;
  float fact1, fact2, total;
  float welfare;

  // Welfare has a different value when pollution is on
  fakeSugar = currentSugar + su/(1+pol);
  fakeSpice = currentSpice + sp/(1+pol);

  // Welfare has a different value when trading and culture are on
  if (allowTrading == 0 || transmitCulture == 0) {
    fact1 = 1;
    fact2 = 1;
  }
  else {
    fact1 = [self getTagNumberOfZeros];
    fact1 /= 11;
    fact2 = 1 - fact1;
  }
  total = (sugarMetabolism * fact1 + spiceMetabolism * fact2);

  // Welfare has a different value when the agents have a foresight
  fakeSugar -= foresight * sugarMetabolism;
  if (fakeSugar < 0) fakeSugar = 0;
  fakeSpice -= foresight * spiceMetabolism;
  if (fakeSpice < 0) fakeSpice = 0;

  welfare = pow(fakeSugar, 
               sugarMetabolism * fact1 / total) * 
            pow(fakeSpice, 
               spiceMetabolism * fact2 / total);
  return welfare;
}

- (float)getMrsWithSugar: (SugarValue)su withSpice: (SpiceValue)sp
{
  float mrs;
  mrs = ((currentSpice + sp)/spiceMetabolism)/
        ((currentSugar + su)/sugarMetabolism);
  return mrs;
}

- (double)getSumPrices
{
  return turnSumPrices;
}

- (double)getProdPrices
{
  return turnProdPrices;
}

- (float)getVolume
{
  return turnVolume;
}

- (int)getNoTrades
{
  return turnTrades;
}

- (int)getSickness
{
  int sickness;
  if (numActDiseases > 0) sickness = 1;
  else sickness = 0;
  return sickness;
}

- (float)getDistance
{
  float dist;
  dist = sqrt((x-initX)*(x-initX)+(y-initY)*(y-initY));
  return dist;
}

// Graphics code, as defined in ObserverSwarm - 100 for red, 200 for blue
//   and 150 for green
- drawSelfOn: (id <Raster>)r
{ 
  if (strcmp([modelSwarm getColorsFor], "sex") == 0) {
    // 0's for F and 1's for M
    if (sex == 0) [r drawPointX: x Y: y Color: 100];
    else [r drawPointX: x Y: y Color: 200];
  }
  
  else if (strcmp([modelSwarm getColorsFor], "culture") == 0) {
    // 0's are red and 1's are blue, different from the book
    if ([self getTribe] == 0) [r drawPointX: x Y: y Color: 100];
    else [r drawPointX: x Y: y Color: 200];
  }
 
  else if (strcmp([modelSwarm getColorsFor], "marked") == 0) {
    // 0's are normal, 1's are marked
    if (marked == 1) [r drawPointX: x Y: y Color: 200];
    else [r drawPointX: x Y: y Color: 100];
  }

  else if (strcmp([modelSwarm getColorsFor], "disease") == 0) {
    if (numActDiseases == 0) [r drawPointX: x Y: y Color: 150];
    else [r drawPointX: x Y: y Color: 100];
  }
 
  else [r drawPointX: x Y: y Color: 100];
  
  return self;
}

@end
